#include "VirtualDisplayWidget.h"
#include <vtkObject.h>
#include <vtkObjectFactory.h>
#include "vtkKWRenderWidget.h"
#include "vtkMRMLDisplayableNode.h"
#include <vtkMRMLTransformNode.h>
#include <vtkMRMLLinearTransformNode.h>
#include <vtkImageActor.h>
#include <vtkMRMLProceduralColorNode.h>
#include <vtkProperty.h>
#include <vtkTexture.h>
#include <vtkRenderer.h>
#include <vtkMRMLVolumeNode.h>
#include <vtkImageActor.h>
#include <vtkAxesActor.h>
#include <vtkAxisActor.h>
#include <vtkCameraWidget.h>
#include <vtkPointWidget.h>
vtkStandardNewMacro ( vtkVirtualDisplayWidget );
vtkCxxRevisionMacro(vtkVirtualDisplayWidget, "$Revision: 1.0 $");
vtkVirtualDisplayWidget::vtkVirtualDisplayWidget(void)
{
	MainViewer = NULL;
	Logic = NULL;
	ProcessingMRMLEvent = 0;
	RenderPending = 0;
	UpdateFromMRMLRequested = 0;
	SceneClosing = false;
	IsResetCamera = 1;
}

vtkVirtualDisplayWidget::~vtkVirtualDisplayWidget(void)
{
	if (MainViewer != NULL)
	{
		MainViewer->SetParent(NULL);
		MainViewer->Delete();
		MainViewer = NULL;
	}
	SetLogic(NULL);
}

void vtkVirtualDisplayWidget::CreateWidget()
{
	if (this->IsCreated())
	{
		vtkErrorMacro(<< this->GetClassName() << " already created");
		return;
	}

	// Call the superclass to create the whole widget

	this->Superclass::CreateWidget();
	MainViewer = vtkKWRenderWidget::New();
	MainViewer->SetParent(this);
	MainViewer->Create();
	MainViewer->SetRendererBackgroundColor(.2, .3, .7);
	//vtkAxesActor * axesActor = vtkAxesActor::New();
	//MainViewer->AddViewProp(axesActor);
	//axesActor->Delete();
	//vtkPointWidget * pointWidget = vtkPointWidget::New();
	//pointWidget->SetCurrentRenderer(MainViewer->GetRenderer());
	//pointWidget->SetInteractor(MainViewer->GetRenderWindowInteractor());
	
	GetApplication()->Script("pack %s -expand true -fill both", MainViewer->GetWidgetName());
}


void vtkVirtualDisplayWidget::UpdateModelsFromMRML(void)
{
	vtkMRMLScene *scene = this->GetMRMLScene();
	vtkMRMLNode *node = NULL;
	std::vector<vtkMRMLDisplayableNode *> slices;

	// find volume slices
	bool clearDisplayedModels = false;

	std::vector<vtkMRMLNode *> dnodes;
	int nnodes = scene->GetNodesByClass("vtkMRMLDisplayableNode", dnodes);
	for (int n=0; n<nnodes; n++)
	{
		vtkMRMLDisplayableNode *model = vtkMRMLDisplayableNode::SafeDownCast(dnodes[n]);
		// render slices last so that transparent objects are rendered in fron of them
		if (model)
		{
			if (!strcmp(model->GetName(), "Red Volume Slice") ||
				!strcmp(model->GetName(), "Green Volume Slice") ||
				!strcmp(model->GetName(), "Yellow Volume Slice"))
			{
				continue;
			}
			this->UpdateModifiedModel(model);
		}
	} // end while
}

void vtkVirtualDisplayWidget::UpdateModelPolyData(vtkMRMLDisplayableNode * model)
{
	std::vector< vtkMRMLDisplayNode *> displayNodes = GetDisplayNode(model);

	for (unsigned int i=0; i<displayNodes.size(); i++)
	{
		vtkMRMLDisplayNode *modelDisplayNode = displayNodes[i];
		vtkProp3D* prop = NULL;
		bool hasPolyData = true;

		int clipping = modelDisplayNode->GetClipping();
		int visibility = modelDisplayNode->GetVisibility();
		vtkPolyData *poly = modelDisplayNode->GetPolyData();

		if (poly == NULL)
		{
			poly = model->GetPolyData();
		}
		if (poly == NULL)
		{
			hasPolyData = false;
		}

		std::map<std::string, vtkProp3D *>::iterator ait;
		ait = this->DisplayedActors.find(modelDisplayNode->GetID());
		if (ait == this->DisplayedActors.end() )
		{
				prop = vtkActor::New();
		}
		else
		{
			prop = (*ait).second;
		}

		vtkActor *actor;
		actor = vtkActor::SafeDownCast(prop);
		if ( actor )
		{
			vtkPolyDataMapper *mapper = vtkPolyDataMapper::New ();	
			mapper->SetInput ( poly );
			actor->SetMapper( mapper );
			mapper->Delete();
		}

		if (hasPolyData && ait == this->DisplayedActors.end())
		{
			this->MainViewer->AddViewProp( prop );
			this->DisplayedActors[modelDisplayNode->GetID()] = prop;
			this->DisplayedNodes[modelDisplayNode->GetID()] = modelDisplayNode;
			prop->Delete();
		}
		else if (!hasPolyData && ait == this->DisplayedActors.end())
		{
			prop->Delete();
		}
		else 
		{
			
		}
	}
}

void vtkVirtualDisplayWidget::UpdateModel(vtkMRMLDisplayableNode * model)
{

	this->UpdateModelPolyData(model);
   
	// observe polydata
	if (!model->HasObserver( vtkMRMLDisplayableNode::PolyDataModifiedEvent, this->MRMLCallbackCommand ))
	{
		model->AddObserver ( vtkMRMLDisplayableNode::PolyDataModifiedEvent, this->MRMLCallbackCommand );
		DisplayableNodes[model->GetID()] = model;
	}
	// observe display node
	if (!model->HasObserver ( vtkMRMLDisplayableNode::DisplayModifiedEvent, this->MRMLCallbackCommand ))
	{
		model->AddObserver ( vtkMRMLDisplayableNode::DisplayModifiedEvent, this->MRMLCallbackCommand );
	}

	if (!model->HasObserver ( vtkMRMLTransformableNode::TransformModifiedEvent, this->MRMLCallbackCommand ) )
	{
		model->AddObserver ( vtkMRMLTransformableNode::TransformModifiedEvent, this->MRMLCallbackCommand );
	}

	vtkMRMLVolumeNode * vnode = vtkMRMLVolumeNode::SafeDownCast(model);
	if (vnode)
	{
		UpdateModelImageData(vnode);
		if (!vnode->HasObserver ( vtkMRMLVolumeNode::ImageDataModifiedEvent, this->MRMLCallbackCommand ) )
		{
			model->AddObserver ( vtkMRMLVolumeNode::ImageDataModifiedEvent, this->MRMLCallbackCommand );
		}
	}
}

void vtkVirtualDisplayWidget::SetModelDisplayProperty(vtkMRMLDisplayableNode * model)
{
	vtkMRMLTransformNode* tnode = model->GetParentTransformNode();
	vtkMatrix4x4* transformToWorld = vtkMatrix4x4::New();
	transformToWorld->Identity();
	if (tnode != NULL && tnode->IsLinear())
	{
		vtkMRMLLinearTransformNode *lnode = vtkMRMLLinearTransformNode::SafeDownCast(tnode);
		lnode->GetMatrixTransformToWorld(transformToWorld);
	}

	std::vector<vtkMRMLDisplayNode *> dnodes = this->GetDisplayNode(model);
	//vtkMRMLDisplayNode *hdnode = this->GetHierarchyDisplayNode(model);

	for (unsigned int i=0; i<dnodes.size(); i++)
	{
		vtkMRMLDisplayNode *dnode = dnodes[i];
		vtkMRMLDisplayNode *mdnode = dnode;
		if (dnode != NULL)
		{
			vtkProp3D *prop = this->GetActorByID(dnode->GetID());
			if (prop == NULL)
			{
				continue;
			}
			// use hierarchy dispaly node if it exists
			/*if (hdnode)
			{
				dnode = hdnode;
			}*/

			vtkActor *actor = vtkActor::SafeDownCast(prop);
			vtkImageActor *imageActor = vtkImageActor::SafeDownCast(prop);
			prop->SetUserMatrix(transformToWorld);
			prop->SetVisibility(mdnode->GetVisibility());
			this->DisplayedVisibility[dnode->GetID()] = mdnode->GetVisibility();

			if (actor)
			{
				actor->GetMapper()->SetScalarVisibility(mdnode->GetScalarVisibility());

				// if the scalars are visible, set active scalars, try to get the lookup
				// table
				if (mdnode->GetScalarVisibility())
				{
					if (mdnode->GetColorNode() != NULL)
					{
						if (mdnode->GetColorNode()->GetLookupTable() != NULL)
						{
							actor->GetMapper()->SetLookupTable(mdnode->GetColorNode()->GetLookupTable());
						}
						else if (mdnode->GetColorNode()->IsA("vtkMRMLProceduralColorNode") &&
							vtkMRMLProceduralColorNode::SafeDownCast(mdnode->GetColorNode())->GetColorTransferFunction() != NULL)
						{
							actor->GetMapper()->SetLookupTable((vtkScalarsToColors*)(vtkMRMLProceduralColorNode::SafeDownCast(mdnode->GetColorNode())->GetColorTransferFunction()));
						}
					}


					int cellScalarsActive = 0;
					if (mdnode->GetActiveScalarName() == NULL)
					{
						// see if there are scalars on the poly data that are not set as
						// active on the display node
						vtkMRMLModelNode *mnode = vtkMRMLModelNode::SafeDownCast(model);
						if (mnode)
						{
							std::string pointScalarName = std::string(mnode->GetActivePointScalarName("scalars"));
							std::string cellScalarName = std::string(mnode->GetActiveCellScalarName("scalars"));
							vtkDebugMacro("Display node active scalar name was null, but the node says active point scalar name = '" << pointScalarName.c_str() << "', cell = '" << cellScalarName.c_str() << "'");
							if (pointScalarName.compare("") != 0)
							{
								vtkWarningMacro("Setting the display node's active scalar to " << pointScalarName.c_str());
								dnode->SetActiveScalarName(pointScalarName.c_str());
							}
							else
							{
								if (cellScalarName.compare("") != 0)
								{
									vtkWarningMacro("Setting the display node's active scalar to " << cellScalarName.c_str());
									dnode->SetActiveScalarName(cellScalarName.c_str());
								}
								else
								{
									vtkDebugMacro("No active scalars");
								}
							}
						}
					}
					if (mdnode->GetActiveScalarName() != NULL)
					{
						vtkMRMLModelNode *mnode = vtkMRMLModelNode::SafeDownCast(model);
						if (mnode)
						{
							mnode->SetActiveScalars(mdnode->GetActiveScalarName(), "Scalars");
							if (strcmp(mdnode->GetActiveScalarName(), mnode->GetActiveCellScalarName("scalars")) == 0)
							{
								cellScalarsActive = 1;
							}
						}
						actor->GetMapper()->SelectColorArray(mdnode->GetActiveScalarName());
					}

					if (!cellScalarsActive)
					{
						// set the scalar range
						actor->GetMapper()->SetScalarRange(mdnode->GetScalarRange());
						if (!(dnode->IsA("vtkMRMLFiberBundleDisplayNode")))
						{
							// WHY need this, does not show glyph colors otherwise
							actor->GetMapper()->SetScalarModeToUsePointFieldData();
						}
						actor->GetMapper()->SetColorModeToMapScalars();            
					}
					else
					{
						actor->GetMapper()->SetScalarModeToUseCellFieldData();
						actor->GetMapper()->SetColorModeToDefault();
						actor->GetMapper()->UseLookupTableScalarRangeOff();
					}
				}
				//// }


				//--- TODO: For now, comment out this backface culling call.
				//--- 06/25/08 we found vtk bug that causes
				//--- vtkTextActors to not display if model backfaceculling
				//--- is on.
				//        actor->GetProperty()->SetBackfaceCulling(dnode->GetBackfaceCulling());
				actor->GetProperty()->SetColor(dnode->GetColor());
				actor->GetProperty()->SetOpacity(dnode->GetOpacity());
				actor->GetProperty()->SetAmbient(dnode->GetAmbient());
				actor->GetProperty()->SetDiffuse(dnode->GetDiffuse());
				actor->GetProperty()->SetSpecular(dnode->GetSpecular());
				actor->GetProperty()->SetSpecularPower(dnode->GetPower());

				if (dnode->GetTextureImageData() != NULL)
				{
					if (actor->GetTexture() == NULL)
					{
						vtkTexture *texture = vtkTexture::New();
						texture->SetInterpolate(0);
						actor->SetTexture(texture);
						texture->Delete();
					}
					actor->GetTexture()->SetInput(dnode->GetTextureImageData());
				}
				else
				{
					actor->SetTexture(NULL);
				}
			}
			else if (imageActor)
			{
				imageActor->SetScale(0.1);
				/*if (dnode->GetTextureImageData() != NULL)
				{
					imageActor->SetInput(dnode->GetTextureImageData());
				}
				else
				{
					imageActor->SetInput(NULL);
				}
				imageActor->SetDisplayExtent(-1, 0, 0, 0, 0, 0);*/
			}
		}
	}
	transformToWorld->Delete();
}

vtkProp3D * vtkVirtualDisplayWidget::GetActorByID(const char * id)
{
	if ( !id )
	{
		return (NULL);
	}

	std::map<std::string, vtkProp3D *>::iterator iter;
	// search for matching string (can't use find, since it would look for 
	// matching pointer not matching content)
	for(iter=this->DisplayedActors.begin(); iter != this->DisplayedActors.end(); iter++) 
	{
		if ( iter->first.c_str() && !strcmp( iter->first.c_str(), id ) )
		{
			return (iter->second);
		}
	}
	return (NULL);
}

std::vector<vtkMRMLDisplayNode *> vtkVirtualDisplayWidget::GetDisplayNode(vtkMRMLDisplayableNode * model)
{
	std::vector< vtkMRMLDisplayNode* > dnodes;
	vtkMRMLDisplayNode* dnode = NULL;

	int ndnodes = model->GetNumberOfDisplayNodes();
	for (int i=0; i<ndnodes; i++)
	{
		dnode = model->GetNthDisplayNode(i);
		if (dnode)
		{
			dnodes.push_back(dnode);
		}
	}

	return dnodes;
}

void vtkVirtualDisplayWidget::UpdateModifiedModel(vtkMRMLDisplayableNode * model)
{
	this->UpdateModel(model);
	this->SetModelDisplayProperty(model);
}

void vtkVirtualDisplayWidget::RemoveModelObservers(vtkMRMLDisplayableNode * model)
{
	if (model != NULL)
	{
		model->RemoveObservers ( vtkMRMLDisplayableNode::PolyDataModifiedEvent, this->MRMLCallbackCommand );
		model->RemoveObservers ( vtkMRMLDisplayableNode::DisplayModifiedEvent, this->MRMLCallbackCommand );
		model->RemoveObservers ( vtkMRMLTransformableNode::TransformModifiedEvent, this->MRMLCallbackCommand );
		vtkMRMLVolumeNode * vmodel = vtkMRMLVolumeNode ::SafeDownCast(model);
		if (vmodel)
		{
			vmodel->RemoveObservers(vtkMRMLVolumeNode::ImageDataModifiedEvent, this->MRMLCallbackCommand);
		}
	}
}

void vtkVirtualDisplayWidget::RemoveModelObservers(int clearCache)
{
	std::map<std::string, vtkMRMLDisplayableNode *>::iterator iter;

	for (iter=this->DisplayableNodes.begin(); iter!=this->DisplayableNodes.end(); iter++)
	{
		if (this->MRMLScene->GetNodeByID(iter->first))
		{
			this->RemoveModelObservers(iter->second);
		}
	}
	if (clearCache)
	{
		this->DisplayableNodes.clear();
		this->DisplayedActors.clear();
		this->DisplayedNodes.clear();
		this->DisplayedClipState.clear();
		this->DisplayedVisibility.clear();
	}
}

void vtkVirtualDisplayWidget::ProcessMRMLEvents(vtkObject * caller, unsigned long event, void * callData)
{
	
	if (this->ProcessingMRMLEvent != 0 )
	{
		return;
	}
	this->ProcessingMRMLEvent = event;
	vtkDebugMacro("ProcessMRMLEvents: processing event " << event);
	if (event == vtkMRMLScene::SceneClosingEvent )
	{
		this->RemoveModelObservers(0);
	}
	else if (event == vtkMRMLScene::SceneCloseEvent )
	{
		this->SceneClosing = true;
		this->ClearupActor();
		this->UpdateFromMRMLRequested = 1;
		this->RequestRender();
		//this->UpdateFromMRML();
		//this->RequestRender();
	}
	else 
	{
		this->SceneClosing = false;
	}

	if ( vtkMRMLScene::SafeDownCast(caller) == this->MRMLScene 
		&& (event == vtkMRMLScene::NodeAddedEvent || event == vtkMRMLScene::NodeRemovedEvent ) )
	{
		vtkMRMLNode *node = (vtkMRMLNode*) (callData);
		if (node != NULL && node->IsA("vtkMRMLDisplayableNode") )
		{
			vtkMRMLDisplayableNode *dnode = vtkMRMLDisplayableNode::SafeDownCast(node);
			if (event == vtkMRMLScene::NodeRemovedEvent)
			{
				this->RemoveDisplayable(dnode);
				this->RequestRender();
			}
			else
			{
				this->UpdateModifiedModel(dnode);
				this->RequestRender( );
				/*this->UpdateFromMRMLRequested = 1;
				this->RequestRender();*/
			}
		}
		else if (node != NULL && node->IsA("vtkMRMLDisplayNode") )
		{
			this->UpdateFromMRMLRequested = 1;
			this->RequestRender();
		}
	}
	else if (vtkMRMLDisplayableNode::SafeDownCast(caller) != NULL)
	{
		// dont reset the camera
		IsResetCamera = 0;
		// check for events on a model node
		vtkMRMLDisplayableNode *modelNode = vtkMRMLDisplayableNode::SafeDownCast(caller);
		if (/*event == vtkCommand::ModifiedEvent ||*/
			/*event == vtkMRMLDisplayableNode::PolyDataModifiedEvent ||*/
			/*event == vtkMRMLVolumeNode::ImageDataModifiedEvent*/
			event == vtkMRMLDisplayableNode::DisplayModifiedEvent || 
			event == vtkMRMLTransformableNode::TransformModifiedEvent)  
		{
			this->SetModelDisplayProperty(modelNode);
			this->RequestRender( );
		}
		else
		{
			this->UpdateModifiedModel(modelNode);
			this->RequestRender();
		}
	}
	else
	{
		vtkDebugMacro("ProcessMRMLEvents: unhandled event " << event << " " << ((event == 31) ? "ModifiedEvent" : "not ModifiedEvent"));
		if (vtkMRMLScene::SafeDownCast(caller) == this->MRMLScene) { vtkDebugMacro("\ton the mrml scene"); }
		if (vtkMRMLNode::SafeDownCast(caller) != NULL) { vtkDebugMacro("\tmrml node id = " << vtkMRMLNode::SafeDownCast(caller)->GetID()); }
	}

	this->ProcessingMRMLEvent = 0;
}

void vtkVirtualDisplayWidget::RequestRender(void)
{
	if (this->GetRenderPending())
	{
		return;
	}
	this->SetRenderPending(1);
	this->Script("after idle \"%s Render\"", this->GetTclName());
}

void vtkVirtualDisplayWidget::UpdateFromMRML(void)
{

	ClearupActor();
	this->UpdateModelsFromMRML();

	this->RequestRender ( );
	this->UpdateFromMRMLRequested = 0;
}

void vtkVirtualDisplayWidget::Render(void)
{

	if (this->UpdateFromMRMLRequested)
	{
		this->UpdateFromMRML();
	}

	if (IsResetCamera)
	{
		this->MainViewer->GetRenderer()->ResetCamera();
	}
	this->MainViewer->Render();
	vtkDebugMacro("vtkSlicerViewerWidget::Render called render" << endl);
	IsResetCamera = 1;
	this->SetRenderPending(0);
}

void vtkVirtualDisplayWidget::ClearupActor(void)
{
	std::map<std::string, vtkProp3D *>::iterator iter;
	for (iter = this->DisplayedActors.begin(); iter != this->DisplayedActors.end(); iter++)
	{
		this->MainViewer->RemoveViewProp(iter->second);
	}
	this->RemoveModelObservers(1);
}

void vtkVirtualDisplayWidget::RemoveDisplayable(vtkMRMLDisplayableNode * model)
{
	if (!model)
	{
		return;
	}  
	int ndnodes = model->GetNumberOfDisplayNodes();
	std::map<std::string, vtkProp3D *>::iterator iter;
	std::vector<std::string> removedIDs;
	for (int i=0; i<ndnodes; i++)
	{
		vtkMRMLDisplayNode *displayNode = model->GetNthDisplayNode(i);
		if (displayNode)
		{
			iter = this->DisplayedActors.find(displayNode->GetID());
			if (iter != this->DisplayedActors.end())
			{
				this->MainViewer->RemoveViewProp(iter->second);
				removedIDs.push_back(iter->first);
			}
		}
	}

	for (unsigned int i=0; i< removedIDs.size(); i++)
	{
		this->RemoveDispalyedID(removedIDs[i]);
	}
	this->RemoveModelObservers(model);
	this->DisplayableNodes.erase(model->GetID());
}

void vtkVirtualDisplayWidget::RemoveDispalyedID(std::string & id)
{
	std::map<std::string, vtkMRMLDisplayNode *>::iterator modelIter;
	this->DisplayedActors.erase(id);
	this->DisplayedClipState.erase(id);
	this->DisplayedVisibility.erase(id);
	this->DisplayedNodes.erase(id);
	//modelIter = this->DisplayedNodes.find(id);
	//if(modelIter != this->DisplayedNodes.end())
	//{
	//	//this->RemoveModelObservers(modelIter->second);
	//	this->DisplayedNodes.erase(modelIter->first);
	//}
}



void vtkVirtualDisplayWidget::UpdateModelImageData(vtkMRMLVolumeNode * vmodel)
{
	std::vector< vtkMRMLDisplayNode *> displayNodes = GetDisplayNode(vmodel);
	for (unsigned int i=0; i<displayNodes.size(); i++)
	{
		vtkMRMLVolumeDisplayNode *modelDisplayNode = vtkMRMLVolumeDisplayNode::SafeDownCast(displayNodes[i]);
		if (modelDisplayNode && !strcmp(modelDisplayNode->GetName(), "EndoscopeDisplayImage3"))
		{
			vtkProp3D* prop = NULL;
			vtkImageData *imageData = modelDisplayNode->GetImageData();
			if (imageData)
			{
				std::map<std::string, vtkProp3D *>::iterator ait;
				ait = this->DisplayedActors.find(modelDisplayNode->GetID());
				if (ait == this->DisplayedActors.end() )
				{
					prop = vtkImageActor::New();
				}
				else
				{
					prop = (*ait).second;
				}
				vtkImageActor *actor;
				actor = vtkImageActor::SafeDownCast(prop);
				if ( actor )
				{
					actor->SetInput(imageData);
				}
				if (ait == this->DisplayedActors.end())
				{
					this->MainViewer->AddViewProp( prop );
					this->DisplayedActors[modelDisplayNode->GetID()] = prop;
					this->DisplayedNodes[modelDisplayNode->GetID()] = modelDisplayNode;
					prop->Delete();
				}
			}
		}
	}
}
